#!/bin/bash
# =============================================================================
# 备份清理脚本 / Backup Cleanup Script
# {{ ansible_managed }}
# 
# 功能说明 / Features:
# - 自动清理过期的备份文件 / Automatically clean up expired backup files
# - 支持不同类型的保留策略 / Support different retention policies
# - 安全删除敏感数据 / Secure deletion of sensitive data
# - 磁盘空间监控和管理 / Disk space monitoring and management
# - 详细的清理日志记录 / Detailed cleanup logging
# - 支持干运行模式 / Support dry-run mode
# 
# 使用方法 / Usage:
#   ./cleanup.sh [auto|manual|emergency] [dry_run]
#   auto     - 自动清理模式 / Automatic cleanup mode
#   manual   - 手动清理模式 / Manual cleanup mode
#   emergency- 紧急清理模式 / Emergency cleanup mode
#   dry_run  - 干运行模式，不实际删除 / Dry run mode, no actual deletion
# 
# 依赖项 / Dependencies:
#   - du, rm, find, shred (安全删除时) / du, rm, find, shred (for secure deletion)
# =============================================================================

# 启用严格错误处理 / Enable strict error handling
set -euo pipefail

# =============================================================================
# 配置参数 / Configuration Parameters
# =============================================================================

# 基础配置 / Basic Configuration
CLEANUP_TYPE="${1:-auto}"                                   # 清理类型 / Cleanup type
BACKUP_BASE_DIR="{{ backup_base_dir }}"                      # 备份基础目录 / Backup base directory
BACKUP_LOG_DIR="{{ backup_log_dir }}"                        # 日志目录 / Log directory
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")                          # 时间戳 / Timestamp
LOG_FILE="${BACKUP_LOG_DIR}/cleanup_${CLEANUP_TYPE}_${TIMESTAMP}.log"  # 日志文件 / Log file
DRY_RUN="${2:-false}"                                        # 干运行模式 / Dry run mode

# 保留策略配置 / Retention Policy Configuration
KEEP_DAILY="{{ backup_cleanup_config.keep_daily }}"          # 保留日备份数量 / Number of daily backups to keep
KEEP_WEEKLY="{{ backup_cleanup_config.keep_weekly }}"        # 保留周备份数量 / Number of weekly backups to keep
KEEP_MONTHLY="{{ backup_cleanup_config.keep_monthly }}"      # 保留月备份数量 / Number of monthly backups to keep
KEEP_YEARLY="{{ backup_cleanup_config.keep_yearly }}"        # 保留年备份数量 / Number of yearly backups to keep

# 安全配置 / Security Configuration
SECURE_DELETE="{{ backup_security_config.secure_delete | lower }}"  # 安全删除开关 / Secure deletion switch
MIN_FREE_SPACE_PERCENT="{{ backup_cleanup_config.min_free_space_percent | default(10) }}"  # 最小剩余空间百分比 / Minimum free space percentage

# 统计计数器 / Statistics Counters
TOTAL_DIRS=0                                                 # 总目录数 / Total directories
DELETED_DIRS=0                                               # 已删除目录数 / Deleted directories
TOTAL_SIZE_DELETED=0                                         # 已删除总大小 / Total size deleted
ERRORS=0                                                     # 错误计数 / Error count

# =============================================================================
# 工具函数 / Utility Functions
# =============================================================================

# 日志记录函数 / Logging function
# 参数: $1 - 日志消息 / Parameters: $1 - log message
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

# 错误退出函数 / Error exit function
# 参数: $1 - 错误消息 / Parameters: $1 - error message
error_exit() {
    log "ERROR: $1"
    exit 1
}

# 用户确认函数 / User confirmation function
# 参数: $1 - 确认消息 / Parameters: $1 - confirmation message
# 功能: 在非干运行模式下要求用户确认操作 / Function: Require user confirmation in non-dry-run mode
confirm_action() {
    local message="$1"
    if [[ "$DRY_RUN" != "true" ]]; then
        echo "$message"
        read -p "Do you want to continue? (yes/no): " -r
        if [[ ! $REPLY =~ ^[Yy][Ee][Ss]$ ]]; then
            log "Cleanup operation cancelled by user"
            exit 0
        fi
    fi
}

# 获取目录大小函数 / Get directory size function
# 参数: $1 - 目录路径 / Parameters: $1 - directory path
# 返回: 目录大小（字节） / Returns: directory size in bytes
get_directory_size() {
    local dir="$1"
    if [[ -d "$dir" ]]; then
        du -sb "$dir" | cut -f1
    else
        echo "0"
    fi
}

# 格式化大小函数 / Format size function
# 参数: $1 - 字节大小 / Parameters: $1 - size in bytes
# 返回: 人类可读的大小格式 / Returns: human-readable size format
format_size() {
    local size="$1"
    if [[ "$size" -gt 1073741824 ]]; then
        echo "$(( size / 1073741824 ))GB"
    elif [[ "$size" -gt 1048576 ]]; then
        echo "$(( size / 1048576 ))MB"
    elif [[ "$size" -gt 1024 ]]; then
        echo "$(( size / 1024 ))KB"
    else
        echo "${size}B"
    fi
}

# 安全删除目录函数 / Secure delete directory function
# 参数: $1 - 目录路径 / Parameters: $1 - directory path
# 功能: 根据配置选择普通删除或安全删除 / Function: Choose normal or secure deletion based on configuration
secure_delete_directory() {
    local dir="$1"
    
    if [[ "$SECURE_DELETE" == "true" ]]; then
        log "Securely deleting directory: $dir"
        
        # 用随机数据覆写文件后删除 / Overwrite files with random data before deletion
        find "$dir" -type f -exec shred -vfz -n 3 {} \; 2>/dev/null || true
        
        # 删除目录 / Remove directory
        rm -rf "$dir"
    else
        log "Deleting directory: $dir"
        rm -rf "$dir"
    fi
}

# 删除备份目录函数 / Delete backup directory function
# 参数: $1 - 目录路径, $2 - 删除原因 / Parameters: $1 - directory path, $2 - deletion reason
# 功能: 删除指定的备份目录并更新统计信息 / Function: Delete specified backup directory and update statistics
delete_backup_directory() {
    local dir="$1"
    local reason="$2"
    
    if [[ ! -d "$dir" ]]; then
        return 0
    fi
    
    local dir_size=$(get_directory_size "$dir")
    
    log "Marking for deletion: $(basename "$dir") ($(format_size "$dir_size")) - $reason"
    
    ((TOTAL_DIRS++))
    
    if [[ "$DRY_RUN" == "true" ]]; then
        log "[DRY RUN] Would delete: $dir"
    else
        if secure_delete_directory "$dir"; then
            log "Successfully deleted: $(basename "$dir")"
            ((DELETED_DIRS++))
            TOTAL_SIZE_DELETED=$((TOTAL_SIZE_DELETED + dir_size))
        else
            log "ERROR: Failed to delete: $dir"
            ((ERRORS++))
        fi
    fi
}

# 从路径提取备份日期函数 / Extract backup date from path function
# 参数: $1 - 备份路径 / Parameters: $1 - backup path
# 返回: 日期字符串（YYYYMMDD_HHMMSS格式）/ Returns: date string in YYYYMMDD_HHMMSS format
get_backup_date_from_path() {
    local path="$1"
    local basename_path=$(basename "$path")
    
    # 提取日期模式 YYYYMMDD_HHMMSS / Extract date pattern YYYYMMDD_HHMMSS
    if [[ "$basename_path" =~ ([0-9]{8}_[0-9]{6}) ]]; then
        echo "${BASH_REMATCH[1]}"
    else
        echo ""
    fi
}

# 获取备份时间戳函数 / Get backup epoch function
# 参数: $1 - 备份日期字符串 / Parameters: $1 - backup date string
# 返回: Unix时间戳 / Returns: Unix timestamp
get_backup_epoch() {
    local backup_date="$1"
    
    if [[ -n "$backup_date" ]] && [[ "$backup_date" =~ ^[0-9]{8}_[0-9]{6}$ ]]; then
        local date_part="${backup_date:0:8}"     # 日期部分 / Date part
        local time_part="${backup_date:9:6}"     # 时间部分 / Time part
        
        # 格式化为标准日期时间格式 / Format to standard datetime format
        local formatted_date="${date_part:0:4}-${date_part:4:2}-${date_part:6:2}"
        local formatted_time="${time_part:0:2}:${time_part:2:2}:${time_part:4:2}"
        
        date -d "$formatted_date $formatted_time" +%s 2>/dev/null || echo "0"
    else
        echo "0"
    fi
}

# 清理日备份函数 / Cleanup daily backups function
# 功能: 清理超过保留期限的日备份文件 / Function: Clean up daily backups exceeding retention period
cleanup_daily_backups() {
    log "Cleaning up daily backups (keeping last $KEEP_DAILY)"
    
    local daily_dir="${BACKUP_BASE_DIR}/daily"
    
    if [[ ! -d "$daily_dir" ]]; then
        log "No daily backup directory found"
        return 0
    fi
    
    # 查找所有日备份目录，按日期排序（最新的在前）/ Find all daily backup directories, sorted by date (newest first)
    local backup_dirs=()
    while IFS= read -r -d '' dir; do
        backup_dirs+=("$dir")
    done < <(find "$daily_dir" -maxdepth 2 -type d -name "*" | sort -r | head -n +100 | tr '\n' '\0')
    
    local count=0
    for dir in "${backup_dirs[@]}"; do
        if [[ "$(basename "$dir")" =~ ^[0-9]{8}_[0-9]{6}$ ]]; then
            ((count++))
            if [[ "$count" -gt "$KEEP_DAILY" ]]; then
                delete_backup_directory "$dir" "exceeds daily retention ($KEEP_DAILY)"
            fi
        fi
    done
}

# 清理周备份函数 / Cleanup weekly backups function
# 功能: 清理超过保留期限的周备份文件 / Function: Clean up weekly backups exceeding retention period
cleanup_weekly_backups() {
    log "Cleaning up weekly backups (keeping last $KEEP_WEEKLY)"
    
    local weekly_dir="${BACKUP_BASE_DIR}/weekly"
    
    if [[ ! -d "$weekly_dir" ]]; then
        log "No weekly backup directory found"
        return 0
    fi
    
    # 查找所有周备份目录，按日期排序（最新的在前）/ Find all weekly backup directories, sorted by date (newest first)
    local backup_dirs=()
    while IFS= read -r -d '' dir; do
        backup_dirs+=("$dir")
    done < <(find "$weekly_dir" -maxdepth 2 -type d -name "*" | sort -r | head -n +100 | tr '\n' '\0')
    
    local count=0
    for dir in "${backup_dirs[@]}"; do
        if [[ "$(basename "$dir")" =~ ^[0-9]{8}_[0-9]{6}$ ]]; then
            ((count++))
            if [[ "$count" -gt "$KEEP_WEEKLY" ]]; then
                delete_backup_directory "$dir" "exceeds weekly retention ($KEEP_WEEKLY)"
            fi
        fi
    done
}

# 清理月备份函数 / Cleanup monthly backups function
# 功能: 清理超过保留期限的月备份文件 / Function: Clean up monthly backups exceeding retention period
cleanup_monthly_backups() {
    log "Cleaning up monthly backups (keeping last $KEEP_MONTHLY)"
    
    local monthly_dir="${BACKUP_BASE_DIR}/monthly"
    
    if [[ ! -d "$monthly_dir" ]]; then
        log "No monthly backup directory found"
        return 0
    fi
    
    # 查找所有月备份目录，按日期排序（最新的在前）/ Find all monthly backup directories, sorted by date (newest first)
    local backup_dirs=()
    while IFS= read -r -d '' dir; do
        backup_dirs+=("$dir")
    done < <(find "$monthly_dir" -maxdepth 2 -type d -name "*" | sort -r | head -n +100 | tr '\n' '\0')
    
    local count=0
    for dir in "${backup_dirs[@]}"; do
        if [[ "$(basename "$dir")" =~ ^[0-9]{8}_[0-9]{6}$ ]]; then
            ((count++))
            if [[ "$count" -gt "$KEEP_MONTHLY" ]]; then
                delete_backup_directory "$dir" "exceeds monthly retention ($KEEP_MONTHLY)"
            fi
        fi
    done
}

# 按年龄清理备份函数 / Cleanup backups by age function
# 功能: 根据备份文件的创建时间清理过期备份 / Function: Clean up expired backups based on creation time
cleanup_by_age() {
    log "Cleaning up backups by age"
    
    local current_epoch=$(date +%s)
    
    # 定义各类型备份的年龄限制（秒）/ Define age limits in seconds
    local daily_age_limit=$((KEEP_DAILY * 24 * 3600))
    local weekly_age_limit=$((KEEP_WEEKLY * 7 * 24 * 3600))
    local monthly_age_limit=$((KEEP_MONTHLY * 30 * 24 * 3600))
    local yearly_age_limit=$((KEEP_YEARLY * 365 * 24 * 3600))
    
    # 在每个备份类型目录中按年龄清理 / Clean up by age in each backup type directory
    for backup_type in daily weekly monthly; do
        local backup_type_dir="${BACKUP_BASE_DIR}/${backup_type}"
        
        if [[ ! -d "$backup_type_dir" ]]; then
            continue
        fi
        
        find "$backup_type_dir" -maxdepth 2 -type d -name "*" | while read -r dir; do
            local backup_date=$(get_backup_date_from_path "$dir")
            
            if [[ -n "$backup_date" ]]; then
                local backup_epoch=$(get_backup_epoch "$backup_date")
                
                if [[ "$backup_epoch" -gt 0 ]]; then
                    local age_seconds=$((current_epoch - backup_epoch))
                    local age_days=$((age_seconds / 86400))
                    
                    case "$backup_type" in
                        "daily")
                            if [[ "$age_seconds" -gt "$daily_age_limit" ]]; then
                                delete_backup_directory "$dir" "older than $KEEP_DAILY days (age: $age_days days)"
                            fi
                            ;;
                        "weekly")
                            if [[ "$age_seconds" -gt "$weekly_age_limit" ]]; then
                                delete_backup_directory "$dir" "older than $KEEP_WEEKLY weeks (age: $age_days days)"
                            fi
                            ;;
                        "monthly")
                            if [[ "$age_seconds" -gt "$monthly_age_limit" ]]; then
                                delete_backup_directory "$dir" "older than $KEEP_MONTHLY months (age: $age_days days)"
                            fi
                            ;;
                    esac
                fi
            fi
        done
    done
}

# 清理损坏备份函数 / Cleanup corrupted backups function
# 功能: 检测并清理损坏的备份文件 / Function: Detect and clean up corrupted backup files
cleanup_corrupted_backups() {
    log "Cleaning up corrupted backups"
    
    # 检查tar.gz文件的完整性 / Check integrity of tar.gz files
    find "$BACKUP_BASE_DIR" -name "*.tar.gz" | while read -r backup_file; do
        if ! tar -tzf "$backup_file" >/dev/null 2>&1; then
            log "Found corrupted backup: $backup_file"
            
            local backup_dir=$(dirname "$backup_file")
            
            # 检查这是否是目录中唯一的文件 / Check if this is the only file in the directory
            local file_count=$(find "$backup_dir" -type f | wc -l)
            
            if [[ "$file_count" -eq 1 ]]; then
                delete_backup_directory "$backup_dir" "contains corrupted backup file"
            else
                log "Removing corrupted file: $backup_file"
                if [[ "$DRY_RUN" != "true" ]]; then
                    rm -f "$backup_file"
                fi
            fi
        fi
    done
    
    # 检查gzip文件的完整性 / Check integrity of gzip files
    find "$BACKUP_BASE_DIR" -name "*.gz" ! -name "*.tar.gz" | while read -r backup_file; do
        if ! gzip -t "$backup_file" 2>/dev/null; then
            log "Found corrupted gzip file: $backup_file"
            if [[ "$DRY_RUN" != "true" ]]; then
                rm -f "$backup_file"
            fi
        fi
    done
}

# 清理空目录函数 / Cleanup empty directories function
# 功能: 清理备份目录中的空目录 / Function: Clean up empty directories in backup directory
cleanup_empty_directories() {
    log "Cleaning up empty directories"
    
    find "$BACKUP_BASE_DIR" -type d -empty | while read -r empty_dir; do
        # 不删除基础备份目录 / Don't delete the base backup directories
        if [[ "$empty_dir" != "$BACKUP_BASE_DIR" ]] && \
           [[ "$empty_dir" != "${BACKUP_BASE_DIR}/daily" ]] && \
           [[ "$empty_dir" != "${BACKUP_BASE_DIR}/weekly" ]] && \
           [[ "$empty_dir" != "${BACKUP_BASE_DIR}/monthly" ]]; then
            
            log "Removing empty directory: $empty_dir"
            if [[ "$DRY_RUN" != "true" ]]; then
                rmdir "$empty_dir" 2>/dev/null || true
            fi
        fi
    done
}

# 检查磁盘空间函数 / Check disk space function
# 功能: 检查备份目录的磁盘空间，必要时触发紧急清理 / Function: Check disk space of backup directory, trigger emergency cleanup if needed
check_disk_space() {
    log "Checking disk space"
    
    local available_percent=$(df "$BACKUP_BASE_DIR" | awk 'NR==2 {print int($4/$2*100)}')
    
    log "Available disk space: ${available_percent}%"
    
    if [[ "$available_percent" -lt "$MIN_FREE_SPACE_PERCENT" ]]; then
        log "WARNING: Low disk space (${available_percent}% < ${MIN_FREE_SPACE_PERCENT}%)"
        log "Performing aggressive cleanup to free space"
        
        # 临时减少保留期限进行紧急清理 / Reduce retention temporarily for emergency cleanup
        KEEP_DAILY=$((KEEP_DAILY / 2))
        KEEP_WEEKLY=$((KEEP_WEEKLY / 2))
        KEEP_MONTHLY=$((KEEP_MONTHLY / 2))
        
        log "Temporarily reduced retention: daily=$KEEP_DAILY, weekly=$KEEP_WEEKLY, monthly=$KEEP_MONTHLY"
        
        return 1  # 表示需要紧急清理 / Indicate emergency cleanup needed
    fi
    
    return 0
}

# 生成清理报告函数 / Generate cleanup report function
# 功能: 生成详细的清理操作报告 / Function: Generate detailed cleanup operation report
generate_cleanup_report() {
    log "Generating cleanup report"
    
    local report_file="${BACKUP_LOG_DIR}/cleanup_report_${TIMESTAMP}.txt"
    
    {
        echo "Backup Cleanup Report"
        echo "====================="
        echo "Timestamp: $(date)"
        echo "Cleanup Type: $CLEANUP_TYPE"
        echo "Dry Run: $DRY_RUN"
        echo "Hostname: $(hostname)"
        echo ""
        echo "Retention Settings:"
        echo "------------------"
        echo "Daily backups: $KEEP_DAILY"
        echo "Weekly backups: $KEEP_WEEKLY"
        echo "Monthly backups: $KEEP_MONTHLY"
        echo "Yearly backups: $KEEP_YEARLY"
        echo ""
        echo "Cleanup Results:"
        echo "---------------"
        echo "Total directories processed: $TOTAL_DIRS"
        echo "Directories deleted: $DELETED_DIRS"
        echo "Total size freed: $(format_size "$TOTAL_SIZE_DELETED")"
        echo "Errors encountered: $ERRORS"
        echo ""
        echo "Current Disk Usage:"
        echo "------------------"
        df -h "$BACKUP_BASE_DIR"
        echo ""
        echo "Remaining Backups:"
        echo "------------------"
        find "$BACKUP_BASE_DIR" -maxdepth 3 -type d -name "*" | sort
        echo ""
        echo "Detailed Log: $LOG_FILE"
    } > "$report_file"
    
    log "Cleanup report generated: $report_file"
}

# 主执行函数 / Main execution function
# 功能: 协调整个清理过程，根据清理类型执行相应的清理操作 / Function: Orchestrate the entire cleanup process, execute appropriate cleanup operations based on cleanup type
main() {
    log "Starting backup cleanup ($CLEANUP_TYPE)..."
    
    # 创建日志目录 / Create log directory
    mkdir -p "$BACKUP_LOG_DIR"
    
    # 检查备份目录是否存在 / Check if backup directory exists
    if [[ ! -d "$BACKUP_BASE_DIR" ]]; then
        error_exit "Backup directory does not exist: $BACKUP_BASE_DIR"
    fi
    
    # 检查磁盘空间并确定是否需要紧急清理 / Check disk space and determine if emergency cleanup is needed
    local emergency_cleanup=false
    if ! check_disk_space; then
        emergency_cleanup=true
    fi
    
    # 根据清理类型执行相应操作 / Execute appropriate operations based on cleanup type
    case "$CLEANUP_TYPE" in
        "auto")
            if [[ "$emergency_cleanup" == "true" ]]; then
                log "Performing emergency cleanup due to low disk space"
            fi
            
            cleanup_corrupted_backups
            cleanup_daily_backups
            cleanup_weekly_backups
            cleanup_monthly_backups
            cleanup_empty_directories
            ;;
        "age")
            confirm_action "This will delete backups based on age limits."
            cleanup_by_age
            cleanup_empty_directories
            ;;
        "corrupted")
            confirm_action "This will delete corrupted backup files."
            cleanup_corrupted_backups
            cleanup_empty_directories
            ;;
        "manual")
            confirm_action "This will perform manual cleanup based on retention settings."
            cleanup_daily_backups
            cleanup_weekly_backups
            cleanup_monthly_backups
            cleanup_corrupted_backups
            cleanup_empty_directories
            ;;
        "emergency")
            log "Performing emergency cleanup - reducing all retention by 50%"
            KEEP_DAILY=$((KEEP_DAILY / 2))
            KEEP_WEEKLY=$((KEEP_WEEKLY / 2))
            KEEP_MONTHLY=$((KEEP_MONTHLY / 2))
            
            cleanup_corrupted_backups
            cleanup_daily_backups
            cleanup_weekly_backups
            cleanup_monthly_backups
            cleanup_empty_directories
            ;;
        *)
            error_exit "Unknown cleanup type: $CLEANUP_TYPE. Available types: auto, age, corrupted, manual, emergency"
            ;;
    esac
    
    # 生成报告 / Generate report
    generate_cleanup_report
    
    # 最终状态 / Final status
    if [[ "$ERRORS" -eq 0 ]]; then
        log "✓ Backup cleanup completed successfully"
        log "Deleted $DELETED_DIRS directories, freed $(format_size "$TOTAL_SIZE_DELETED")"
    else
        log "⚠ Backup cleanup completed with $ERRORS errors"
    fi
    
    # 检查最终磁盘空间 / Check final disk space
    check_disk_space || log "WARNING: Disk space is still low after cleanup"
}

# 执行主函数 / Execute main function
main "$@"